﻿using System.Linq;
using IInject.Registration;
using IInject.Resolution;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IInject.Tests
{
	[TestClass]
	public class ComplexContainerTests
	{
		[TestMethod]
		public void DefaultConstructorSelectorTest()
		{
			var container = new Container();
			container.Map<Dependent>().Apply();
			var dependency = new TypeDependency(new RegistryContext
			{
				ToType = typeof(DependencyTest)
			});
			var cons = new MostResolvableConstructorSelector(container).Select(dependency);
			Assert.AreEqual(cons.GetParameters().Count(), 1);
		}

		[TestMethod]
		public void RegisterTest()
		{
			var container = new Container();
			container.Map<Dependent>().Apply();
			var test = container.Map<DependencyTest>().Apply().Get<DependencyTest>();
			Assert.IsNotNull(test);
			Assert.IsNotNull(test.Child);
		}

		[TestMethod]
		public void RegisterWithDependenciesTest()
		{
			var container = new Container();
			container.Map<Dependent>().Apply();
			container.Map<DependencyTest>().Apply();
			var test = container.Get<DependencyTest>();
			Assert.IsNotNull(test);
		}

		[TestMethod]
		public void RegisterWithValueTypeDependenciesTest()
		{
			var c = new Container();
			c.Map<int>().Return(10).Apply();
			c.Map<string>().Return("Test").Apply();
			var test = c.Map<DependencyTest>().Apply().Get<DependencyTest>();
			Assert.IsNotNull(test);
		}

		[TestMethod]
		public void ObjectBuilderInjectTest()
		{
			var container = new Container();
			ContainerProvider.SetCurrent(container);
			container.Set<Dependent>();
			var test = ObjectBuilder.Inject<DependencyTest>();
			Assert.IsNotNull(test);
			Assert.IsNotNull(test.Child);
		}

		[TestMethod]
		public void ObjectBuilderInjectTest2()
		{
			var container = new Container();
			ContainerProvider.SetCurrent(container);
			container.Map<int>().Return(10).Apply();
			container.Map<string>().Return("Test").Apply();
			var test = ObjectBuilder.Inject<DependencyTest>();
			Assert.IsNotNull(test);
			Assert.AreEqual(test.Id, 10);
			Assert.AreEqual(test.Name, "Test");
		}

		[TestMethod]
		public void ObjectBuilderInjectTest3()
		{
			var container = new Container();
			ContainerProvider.SetCurrent(container);
			container.Set<DependencyTest>();
			container.Set<IDependencyTest, DependencyTest>();
			container.Set<Dependent>();
			container.Map<int>().Return(10).Apply();
			var test = ObjectBuilder.Inject<DependencyTest>();
			var parentTest = ObjectBuilder.Inject<ParentDependent>();
			Assert.IsNotNull(test);
			Assert.IsNotNull(parentTest);
			Assert.IsNotNull(parentTest.Dependency);
			Assert.AreEqual(parentTest.Dependency.Child.Id, 10);
		}

		[TestMethod]
		public void SelectMostResolvableConstructorTest()
		{
			var container = new Container();
			ContainerProvider.SetCurrent(container);
			container.Set<Dependent>();
			container.Map<int>(a => a.Return(10));
			container.Map<string>(a => a.Return("Name"));
			var dependent = ObjectBuilder.Inject<Dependent>();
			var test = ObjectBuilder.Inject<DependencyTest>();
			Assert.IsNotNull(dependent);
			Assert.AreEqual(dependent.Id, 10);
			Assert.IsNotNull(test);
			Assert.AreEqual(test.Id, 10);
			Assert.AreEqual(test.Name, "Name");
		}

		[TestMethod]
		public void SelectPropertyForInjectionTest()
		{
			var container = new Container();
			container.Set<Dependent>();
			container.Map<string>(a => a.Return("Name"));
			container.Map<DependencyTest>(m => m.Inject(t => t.Name));
			var test = container.Get<DependencyTest>();
			Assert.IsNotNull(test);
			Assert.AreEqual(test.Name, "Name");
		}

		[TestMethod]
		public void SelectPropertyWithValueForInjectionTest()
		{
			var container = new Container();
			var dependent = new Dependent();
			container.Map<DependencyTest>(m =>
			{
				m.Inject(t => t.Name, "Name");
				m.Inject(t => t.Child, dependent);
			});
			var test = container.Get<DependencyTest>();
			Assert.IsNotNull(test);
			Assert.AreEqual(test.Name, "Name");
			Assert.AreEqual(test.Child, dependent);
		}

		[TestMethod]
		public void SelectPropertyForInjectionTest2()
		{
			// Create a new Container.
			var container = new Container();
			// Register the objects in the container.
			container.Set<Dependent>().Set<IInjectedProperty, InjectedProperty>();
			container.Map<IInjectedProperty>(m => m.To<InjectedProperty>());
			container.Map<int>(a => a.Return(1000));
			container.Map(typeof(ParentDependent)).With("Key").Apply();
			container.Map<IDependencyTest>(m =>
			{
				// Map the interface to the concrete type.
				m.To<DependencyTest>();
				// Set these properties to be injected.
				m.Inject(t => t.Id);
				m.Inject(t => t.Property);
			});
			// Retrieve the objects from the container.
			var test = container.Get<IDependencyTest>();
			var parent = container.Get<ParentDependent>("Key");
			// Verify all the items were set correctly
			// and the properties were injected with the proper values.
			Assert.IsNotNull(test);
			Assert.AreEqual(test.Id, 1000);
			Assert.IsNotNull(test.Property);
			Assert.IsNotNull(test.Property.Dependent);
			Assert.AreEqual(test.Property.Dependent.Id, 1000);
			Assert.IsNotNull(parent);
			Assert.AreNotEqual(parent.Dependency, test);
		}
	}
}
